"""
输入清理和安全验证模块
"""
import re
import html
import logging
from typing import Any, Dict, List, Union
from urllib.parse import urlparse

logger = logging.getLogger(__name__)

class InputSanitizer:
    """输入数据清理和安全验证器"""
    
    def __init__(self):
        """初始化清理器"""
        self.xss_patterns = [
            r'<script.*?>.*?</script>',
            r'javascript:',
            r'on\w+\s*=',
            r'<iframe.*?>.*?</iframe>',
            r'<object.*?>.*?</object>',
            r'<embed.*?>.*?</embed>',
            r'<link.*?>',
            r'<meta.*?>',
            r'<style.*?>.*?</style>',
        ]
        
        self.sql_injection_patterns = [
            r"'\s*;\s*drop\s+table",
            r"'\s*;\s*delete\s+from",
            r"union\s+select",
            r"'\s*or\s+'1'\s*=\s*'1",
            r"'\s*or\s+1\s*=\s*1",
            r"--\s*$",
            r"/\*.*?\*/",
        ]
        
        self.nosql_injection_patterns = [
            r"\$ne\s*:",
            r"\$gt\s*:",
            r"\$where\s*:",
            r"\$regex\s*:",
            r"this\.",
            r"db\.",
        ]
        
        self.dangerous_tags = [
            'script', 'iframe', 'object', 'embed', 'form', 'input',
            'textarea', 'button', 'select', 'option', 'link', 'meta',
            'style', 'base', 'title', 'head', 'html', 'body'
        ]
        
        self.dangerous_attributes = [
            'onclick', 'onload', 'onerror', 'onmouseover', 'onmouseout',
            'onfocus', 'onblur', 'onchange', 'onsubmit', 'onreset',
            'onselect', 'onkeydown', 'onkeyup', 'onkeypress', 'onabort'
        ]
    
    def sanitize_string(self, text: str, max_length: int = 10000) -> str:
        """
        清理字符串内容
        
        Args:
            text: 原始文本
            max_length: 最大长度限制
            
        Returns:
            清理后的安全文本
        """
        if not isinstance(text, str):
            return ""
        
        # 长度限制
        if len(text) > max_length:
            text = text[:max_length]
        
        # 移除控制字符
        text = ''.join(char for char in text if ord(char) >= 32 or char in '\n\r\t')
        
        # HTML解码
        text = html.unescape(text)
        
        # 移除潜在的XSS内容
        for pattern in self.xss_patterns:
            text = re.sub(pattern, '', text, flags=re.IGNORECASE | re.DOTALL)
        
        return text.strip()
    
    def sanitize_html(self, html_content: str) -> str:
        """
        清理HTML内容，移除危险标签和属性
        
        Args:
            html_content: HTML内容
            
        Returns:
            清理后的安全HTML
        """
        if not html_content:
            return ""
        
        # 移除危险标签
        for tag in self.dangerous_tags:
            pattern = rf'<{tag}[^>]*>.*?</{tag}>'
            html_content = re.sub(pattern, '', html_content, flags=re.IGNORECASE | re.DOTALL)
            
            # 移除自闭合标签
            pattern = rf'<{tag}[^>]*/?>'
            html_content = re.sub(pattern, '', html_content, flags=re.IGNORECASE)
        
        # 移除危险属性
        for attr in self.dangerous_attributes:
            pattern = rf'{attr}\s*=\s*["\'][^"\']*["\']'
            html_content = re.sub(pattern, '', html_content, flags=re.IGNORECASE)
        
        # 移除javascript:协议
        html_content = re.sub(r'href\s*=\s*["\']javascript:[^"\']*["\']', '', html_content, flags=re.IGNORECASE)
        
        return html_content
    
    def detect_sql_injection(self, text: str) -> bool:
        """
        检测SQL注入攻击
        
        Args:
            text: 待检测文本
            
        Returns:
            是否包含SQL注入
        """
        if not isinstance(text, str):
            return False
        
        text_lower = text.lower()
        
        for pattern in self.sql_injection_patterns:
            if re.search(pattern, text_lower, re.IGNORECASE):
                logger.warning(f"检测到SQL注入攻击: {pattern}")
                return True
        
        return False
    
    def detect_nosql_injection(self, data: Union[str, Dict, List]) -> bool:
        """
        检测NoSQL注入攻击
        
        Args:
            data: 待检测数据
            
        Returns:
            是否包含NoSQL注入
        """
        if isinstance(data, str):
            text_lower = data.lower()
            for pattern in self.nosql_injection_patterns:
                if re.search(pattern, text_lower):
                    logger.warning(f"检测到NoSQL注入攻击: {pattern}")
                    return True
        
        elif isinstance(data, dict):
            for key, value in data.items():
                if isinstance(key, str) and key.startswith('$'):
                    logger.warning(f"检测到NoSQL操作符: {key}")
                    return True
                if self.detect_nosql_injection(value):
                    return True
        
        elif isinstance(data, list):
            for item in data:
                if self.detect_nosql_injection(item):
                    return True
        
        return False
    
    def sanitize_url(self, url: str) -> str:
        """
        清理URL，移除危险协议
        
        Args:
            url: 原始URL
            
        Returns:
            清理后的安全URL
        """
        if not url:
            return ""
        
        try:
            parsed = urlparse(url)
            
            # 只允许安全协议
            allowed_schemes = ['http', 'https', 'ftp', 'ftps']
            if parsed.scheme.lower() not in allowed_schemes:
                logger.warning(f"危险URL协议: {parsed.scheme}")
                return ""
            
            return url
            
        except Exception as e:
            logger.error(f"URL解析失败: {e}")
            return ""
    
    def validate_data_item(self, item: Dict[str, Any]) -> bool:
        """
        验证数据项是否安全
        
        Args:
            item: 数据项
            
        Returns:
            是否通过安全验证
        """
        try:
            # 检查所有字符串字段
            for key, value in item.items():
                if isinstance(value, str):
                    if self.detect_sql_injection(value):
                        return False
                elif isinstance(value, dict):
                    if not self.validate_data_item(value):
                        return False
                elif isinstance(value, list):
                    for list_item in value:
                        if isinstance(list_item, dict):
                            if not self.validate_data_item(list_item):
                                return False
                        elif isinstance(list_item, str):
                            if self.detect_sql_injection(list_item):
                                return False
            
            # 检查NoSQL注入
            if self.detect_nosql_injection(item):
                return False
            
            return True
            
        except Exception as e:
            logger.error(f"数据验证失败: {e}")
            return False
    
    def sanitize_data_item(self, item: Dict[str, Any]) -> Dict[str, Any]:
        """
        清理数据项中的所有字符串字段
        
        Args:
            item: 原始数据项
            
        Returns:
            清理后的数据项
        """
        clean_item = {}
        
        for key, value in item.items():
            if isinstance(value, str):
                clean_item[key] = self.sanitize_html(self.sanitize_string(value))
            elif isinstance(value, dict):
                clean_item[key] = self.sanitize_data_item(value)
            elif isinstance(value, list):
                clean_list = []
                for list_item in value:
                    if isinstance(list_item, dict):
                        clean_list.append(self.sanitize_data_item(list_item))
                    elif isinstance(list_item, str):
                        clean_list.append(self.sanitize_html(self.sanitize_string(list_item)))
                    else:
                        clean_list.append(list_item)
                clean_item[key] = clean_list
            else:
                clean_item[key] = value
        
        return clean_item

# 全局实例
input_sanitizer = InputSanitizer()